import {deepClone} from './clone';

/**
 * 检查值是否为对象
 * @param item 要检查的值
 * @returns 如果是对象则返回true，否则返回false
 */
export function isObject(item: unknown): item is Record<string, any> {
  return item !== null && typeof item === 'object' && !Array.isArray(item);
}

/**
 * 深度合并两个配置对象
 *
 * @param target 目标对象
 * @param source 来源对象，会覆盖目标对象中的同名属性
 * @returns 合并后的新对象
 *
 * @example
 * ```ts
 * const defaultConfig = {
 *   theme: { color: 'blue', size: 'medium' },
 *   debug: false
 * };
 *
 * const userConfig = {
 *   theme: { color: 'red' }
 * };
 *
 * const mergedConfig = mergeObject(defaultConfig, userConfig);
 * // 结果: { theme: { color: 'red', size: 'medium' }, debug: false }
 * ```
 */
export function mergeObject<T extends Record<string, any>>(
  target: T,
  source?: Partial<T>
): T {
  // 如果用户配置为空或不是对象，则直接返回默认配置的深度克隆
  if (!source || !isObject(source)) {
    return deepClone(target);
  }

  // 创建默认配置的深度克隆作为结果
  const result = deepClone(target);

  // 遍历用户配置中的所有键
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      const userValue = source[key];
      const defaultValue = target[key];

      // 如果两者都是对象，则递归合并
      if (isObject(defaultValue) && isObject(userValue)) {
        result[key] = mergeObject(defaultValue, userValue);
      }
      // 否则直接使用用户的值（包括数组）
      else if (userValue !== undefined) {
        // 对用户提供的值进行深度克隆，避免后续修改影响原始对象
        result[key] = deepClone(userValue);
      }
    }
  }

  return result;
}

/**
 * 深度合并多个配置对象
 *
 * @param target 目标对象
 * @param sources 多个来源对象，按顺序合并，后面的会覆盖前面的
 * @returns 合并后的新对象
 *
 * @example
 * ```ts
 * const defaultConfig = { theme: { color: 'blue' }, debug: false };
 * const userConfig1 = { theme: { size: 'large' } };
 * const userConfig2 = { debug: true };
 *
 * const mergedConfig = mergeObjects(defaultConfig, userConfig1, userConfig2);
 * // 结果: { theme: { color: 'blue', size: 'large' }, debug: true }
 * ```
 */
export function mergeObjects<T extends Record<string, any>>(
  target: T,
  ...sources: Array<Partial<T> | undefined>
): T {
  // 从默认配置的深度克隆开始
  let result = deepClone(target);

  // 依次合并每个配置
  for (const source of sources) {
    if (source && isObject(source)) {
      result = mergeObject(result, source);
    }
  }

  return result;
}
